/*
 * =====================================================================================
 *
 *       Filename:  getAllTasks.c
 *
 *    Description:  Modul ziska vsechny procesy bezici v systemu (vcetne skrytych)
 *                  a poskytne je uzivateli. Jako komunikacni prostredek vyuziva
 *                  soubor v /proc. Seznam procesu ziskava metodami: pruchod skrze
 *                  task list, hledani v pid hash
 *
 *        Created:  17.4.2013 17:29:21
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Radek Necas, xnecas18@stud.fit.vutbr.cz
 *   Organization:  FIT, VUT Brno
 *
 * =====================================================================================
 */


#include <linux/module.h>
#include <linux/kernel.h>

#include <linux/sched.h>

#include <linux/proc_fs.h>
#include <linux/string.h>
#include <linux/vmalloc.h>
#include <linux/uaccess.h>

#include "../../include/const.h"

// Konstanty
#define STARTLEN    40
#define INCREASELEN 20
#define INPUTSIZE   16
#define WRITE_BUFF_LEN 8 

// Globalni promenne
struct task_struct **procs;
int procsCount;
int actProc;
int wasWrite;
char inputBuf[INPUTSIZE];


MODULE_LICENSE("GPL");
MODULE_AUTHOR("Radek Necas");
MODULE_DESCRIPTION("Get all proceses in task list");


/*
 * Funkce naplni globalni promenne vsemi pid, ktera se daji zjistit z pid hash.
 * Globalni promenne: procs - pole s naplnenimi procesy, procsCount - pocet ziskanych procesu
 *                    actProcs - id aktualniho procesu nuluje, wasWriten - nastavi priznak
 */
int fillProcsPidHash(void)
{
    int i;
    struct task_struct *task;
    struct pid pid;
    struct pid *pidPtr = &pid;
    int arrayLen = STARTLEN;
    long taskPtrSize = sizeof(struct task_struct *);
    actProc = 0;
    procsCount = 0;

    procs = (struct task_struct **)kmalloc(arrayLen*taskPtrSize, GFP_KERNEL);
    if(procs == NULL) {
        printk(KERN_INFO "getAllProcs: Chyba pri alokaci pameti \n");
        return 0;
    }

 
    // Ziskani info o vsech procesich
    for(i = 1; i<= MAX_PID; i++) {
        task = pid_task(find_vpid(i), PIDTYPE_PID);
        if(task != NULL &&(task->exit_state != EXIT_DEAD && task->exit_state != EXIT_ZOMBIE)) {
            if(procsCount >= arrayLen) {
                arrayLen += INCREASELEN;
                procs = (struct task_struct **)krealloc(procs, arrayLen*taskPtrSize, GFP_KERNEL);
                if(procs == NULL) {
                    printk(KERN_INFO "getAllProcs: Chyba pri realokaci pameti \n");
                    kfree(procs);
                    procsCount = 0;
                    return 0;
                }
            }
            procs[procsCount] = task;
            procsCount++;
        }
    }



/* 
    do_each_pid_task(pidPtr, PIDTYPE_PID, task) {
        if(procsCount >= arrayLen) {
            arrayLen += INCREASELEN;
            procs = (struct task_struct **)krealloc(procs, arrayLen*taskPtrSize, GFP_KERNEL);
            if(procs == NULL) {
                printk(KERN_INFO "getAllProcs: Chyba pri realokaci pameti \n");
                kfree(procs);
                procsCount = 0;
                return 0;
            }
        }
        procs[procsCount] = task;
        procsCount++;
   
    } while_each_pid_task(pidPtr, PIDTYPE_PID, task);
*/

    wasWrite = 1;
    return procsCount;
}


/*
 * Funkce naplni globalni promenne vsemi pid, ktera se daji zjistit z task list.
 * Globalni promenne: procs - pole s naplnenimi procesy, procsCount - pocet ziskanych procesu
 *                    actProcs - id aktualniho procesu nuluje, wasWriten - nastavi priznak
 */
int fillProcsTaskList(void)
{
    struct task_struct *task;
    int arrayLen = STARTLEN;
    long taskPtrSize = sizeof(struct task_struct *);
    actProc = 0;

    procsCount = 0;
    procs = (struct task_struct **)kmalloc(arrayLen*taskPtrSize, GFP_KERNEL);
    if(procs == NULL) {
        printk(KERN_INFO "getAllProcs: Chyba pri alokaci pameti \n");
        return 0;
    }

    // Ziskani info o vsech procesich
    for_each_process(task) {
        if(procsCount >= arrayLen) {
            arrayLen += INCREASELEN;
            procs = (struct task_struct **)krealloc(procs, arrayLen*taskPtrSize, GFP_KERNEL);
            if(procs == NULL) {
                printk(KERN_INFO "getAllProcs: Chyba pri realokaci pameti \n");
                kfree(procs);
                procsCount = 0;
                return 0;
            }
        }
        procs[procsCount] = task;
        procsCount++;
    }

    wasWrite = 1;
    return procsCount;
}


/*
 * Funkce, ktera bude volna vzdy pri cteni ze souboru.
 * Soubor bude umisten v /proc a slouzi jako komunikacni prostredek mezi modulem
 * a uzivatelskou aplikaci
 */
static int ibpProcsRead(char *page, char **start, off_t off, int count,
        int *eof, void *data)
{
    int len;

    if(off > 0) {
        printk(KERN_INFO "getAllProcs: ibpProcsRead offset vetsi jak 0");
        return 0;
    }

    len = 0;
    if(wasWrite) {      // Pokud se jiz zadalo o zapis -> naplnily se data
        if(actProc <= procsCount) {     
            len=sprintf(page, "%05d\n", procs[actProc]->pid);
            actProc++;
        }
        else {  // Pokud jsem prosel cele pole musi se v dalsi iter znovu zapsat
            printk(KERN_INFO "getALLProcs: Snaha cist mimo meze vsech procesu");
            wasWrite = 0;
            actProc = 0;
            procsCount = 0;
            kfree(procs);
            procs = NULL;
            *eof=1;
        }
    }

    return(len);
}

/*
 * Funkce, ktera bude volna vzdy pri zapisu do souboru.
 * Soubor bude umisten v /proc a slouzi jako komunikacni prostredek mezi modulem
 * a uzivatelskou aplikaci
 */
static int ibpProcsWrite(struct file *file, const char *buffer, 
        unsigned long count, void *data)
{
    char localBuff[WRITE_BUFF_LEN];
    long type;
    int len;

    if(count > WRITE_BUFF_LEN) {
        printk(KERN_INFO "getAllProcs: Chyba ibpProcsWrite() zapis prilis mnoha dat");
        printk(KERN_INFO "");
        return -1;
        
    }

    if(copy_from_user(localBuff, buffer, count)) {
        printk(KERN_INFO "getAllProcs: Chyba ibpProcsWrite() nemuze zkopirovat data");
        printk(KERN_INFO "");
        return -2;
    }
/*  
    if(strict_strtol(localBuff, 10, &type) < 0) {
        printk(KERN_INFO "getAllProcs: Chyba ibpProcsWrite() nemuze prevest cislo");
        return -3;
    }
*/
    type = (int) localBuff[0];
    len = 0;
    if(type == FROM_PIDHASH) {
        len = fillProcsPidHash();
    }
    else if(type == FROM_TASKLIST) {
        len = fillProcsTaskList();
    }

    else {
        printk(KERN_INFO "getAllProcs: Chybny typ ziskani procesu");
        printk(KERN_INFO "");
        return -3;
    }

    actProc = 0;
    wasWrite = 1;


/*
    for(i = 0; i < procsCount; i++) {
        printk(KERN_INFO "getAllProcs: PID:  %d", procs[i]);
    }
    printk(KERN_INFO "getAllProcs: ibpProcsWrite naplnilo %d pid\n", procsCount);
*/
    return len;
}


/* 
 * Inicializacni funkce modulu
 */
int init_module(void)
{
    struct proc_dir_entry *ibpAllProcsFile;

    int i;
    procs = NULL;
    procsCount = 0;
    actProc = 0;
    wasWrite = 0;
  
    ibpAllProcsFile = create_proc_entry(ALL_PROCS_FILENAME, 0666, NULL);
    if(ibpAllProcsFile == NULL) {
        printk(KERN_INFO "getAllProcs: Nepodarilo se vytvorit soubor v proc");
    }

    ibpAllProcsFile->data = inputBuf;
    ibpAllProcsFile->read_proc = ibpProcsRead;
    ibpAllProcsFile->write_proc = ibpProcsWrite;


    printk(KERN_INFO "getAllProcs: Modul zaveden");
    printk(KERN_INFO "");
/*  
    fillProcsPidHash();
    printk(KERN_INFO "getAllProcs: Nalezlo se %d procesu", procsCount);
    printk(KERN_INFO "");
    for(i = 0; i < procsCount; i++) {
//        if(i < 20) {
        printk(KERN_INFO "getAllProcs: Proces: %d[%s]", procs[i]->pid, procs[i]->comm);
        printk(KERN_INFO "");
//        }
    }
*/
    return 0;
}


/*
 * Uklidova funkce modulu.
 */
void cleanup_module(void)
{


    remove_proc_entry(ALL_PROCS_FILENAME, NULL);
    if(procs != NULL)
        kfree(procs);


    printk(KERN_INFO "getAllProcs: Modul odebran");
    printk(KERN_INFO "");
}
